/**
 * 日历插件
 * @author Ly
 * @version 1.0a
 */
/**
 * Ly.Calendar构造函数
 * @param {Object} config 日历配置信息
 * config = {
	 container : document.body,       // 日历容器，默认为body
	 defaultDate : new Date(),        // 默认日期，默认当天
	 acceptor : element,              // 接收器，即日历控件与哪一个元素绑定，input元素
	 formatDate : 'yyyy-mm-dd',       // 返回格式
	 showTimeBar : false,                // 是否显示时间，默认false
	 timeStep : [1, 1, 10]            // 显示时间时的步长
 * }
 */
Ly.Calendar = function(config /* Object */) {
	var _this = this;
	
	config = config || {};
	
	this.handler = {
		win          : null,
		titleBar     : null,
		dateBar      : null,
		timeBar      : null,
		operateBar   : null,
		siblingYear  : null,
		siblingMonth : null,
		year         : null,
		month        : null,
		nextMonth    : null,
		nextYear     : null,
		hour         : null,
		minute       : null,
		second       : null
	};
	this.config = {
		id          : Ly.getCount(),                         // 日历中所有元素的唯一标识
		container   : config.container || document.body,     // 承载日历插件的容器，一般为body
		defaultDate : config.defaultDate || new Date(),      // 默认日期
		acceptor    : config.acceptor || null,               // 日历插件的接收器，一般情况下也是触发器
		formatDate  : config.formatDate || 'yyyy-mm-dd',     // 日历插件返回数据时的格式
		showTimeBar : config.showTimeBar === true,           // 是否显示时间选项，默认不显示
		timeStep    : config.timeStep || [1, 1, 1],          // 时间选项中【时，分，秒】的步长
		currentDate : {},                                    // 当前时间，指当前日历插件所展现的时间
		previous    : null,                                  // 上一次选取的日期
		initAcceptor : config.initAcceptor !== false,        // 是否初始化时就将接收器赋值，默认true
		customEvent  : config.customEvent
	};
	this.private = {
		// 显示日历插件
		showCalendar : function() {
			var config     = _this.config,
				handler    = _this.handler,
				scrollTop  = config.container === document.body ?
							 document.documentElement.scrollTop || document.body.scrollTop :
							 config.container.scrollTop,
				scrollLeft = config.container === document.body ?
							 document.documentElement.scrollLeft || document.body.scrollLeft :
							 config.container.scrollLeft,
				top        = Ly.getY(config.acceptor, config.container) + config.acceptor.clientHeight,
				left       = Ly.getX(config.acceptor, config.container);
			
			handler.win.style.display = 'block';
			
			// 判断高度是否大于边界
			if (top + handler.win.clientHeight > config.container.clientHeight + scrollTop) {
				top = top - handler.win.clientHeight - config.acceptor.clientHeight;
			}
			if (left + handler.win.clientWidth > config.container.clientWidth + scrollLeft) {
				left = left + config.acceptor.clientWidth - handler.win.clientWidth;
			}
			
			handler.win.style.top  = top + 'px';
			handler.win.style.left = left + 'px';

			return this;
		},
		// 隐藏日历插件
		hideCalendar : function() {
			_this.handler.win.style.display = 'none';
			return this;
		},
		// 初始化日历插件
		initialization : function() {
			var dd = _this.config.defaultDate;
			
			_this.config.currentDate = {
				year   : dd.getFullYear(),
				month  : dd.getMonth(),
				date   : dd.getDate(),
				week   : dd.getDay(),
				hour   : this.getT(dd.getHours(), 0),
				minute : this.getT(dd.getMinutes(), 0),
				second : this.getT(dd.getSeconds(), 0)
			};
			
			this.initializationAcceptor().createElement().createTitleBar().createDateBar().createTimeBar().createOperateBar().bindEvents();
			
			_this.config.initAcceptor && this.updateAcceptor(false);
			
			return this;
		},
		// 初始化接收器
		initializationAcceptor : function() {
			var config = _this.config;
			
			//config.acceptor.readOnly = true;
			Ly.addEvent('onclick', function() { _this.show(); }, config.acceptor);
			
			return this;
		},
		/**
		 * 按步长来处理时间
		 * @param {Number} 要处理的时间，时、分、秒
		 * @param {Number} 在步长数组中的索引值
		 * @return {Number} 处理后的时间
		 */
		getT : function(v /* Number */, i /* Number */) {
			var config = _this.config,
				tmp    = (config.timeStep.length > i + 1) ? config.timeStep[i] : 0;
			
			return !tmp ? v : parseInt(v / config.timeStep[i]) * config.timeStep[i];
		},
		/**
		 * 判断obj对象是否为日历插件的子元素
		 * @param {Object} obj 要判断的元素
		 * @return {Boolean} true/false
		 */
		own : function(obj) {
			if (!obj) {
				return false;
			} else if (obj.parentNode === _this.handler.win) {
				return true;
			} else if (obj === document.body) {
				return false;
			} else {
				return arguments.callee(obj.parentNode);
			}
		},
		/**
		 * 更新接收器数据
		 * @param {Boolean} flag 是否立即执行用户自定义事件，默认是
		 */
		updateAcceptor : function(flag) {
			var config = _this.config;
			
			config.acceptor.value = Ly.formatTime(
				new Date(
					config.currentDate.year,
					config.currentDate.month,
					config.currentDate.date,
					config.currentDate.hour,
					config.currentDate.minute,
					config.currentDate.second
				), config.formatDate
			);
			
			flag !== false && config.customEvent && config.customEvent.call(config.acceptor);
			
			return this;
		},
		/**
		 *  更新日历插件的日期
		 * @param {Date} date 日期对象，省略此参数则从config.currentDate中获取
		 */
		updateDate : function(date /* Date */) {
			var config = _this.config,
				handler = _this.handler;
			
			date && (config.currentDate = {
				year   : date.getFullYear(),
				month  : date.getMonth(),
				date   : date.getDate(),
				week   : date.getDay(),
				hour   : this.getT(date.getHours(), 0),
				minute : this.getT(date.getMinutes(), 0),
				second : this.getT(date.getSeconds(), 0)
			});
			
			handler.year.innerHTML  = config.currentDate.year;
			handler.month.innerHTML = Ly.fill(config.currentDate.month + 1);
			
			this.createDateBar().bindEvents(false);
			
			return this;
		},
		/**
		 * 更新日历插件的时间
		 * @param {Date} date 日期对象，省略此参数则从config.currentDate中获取
		 */
		updateTime : function(date /* Date */) {
			var config = _this.config,
				handler = _this.handler;
			
			date && (config.currentDate = {
				year   : date.getFullYear(),
				month  : date.getMonth(),
				date   : date.getDate(),
				week   : date.getDay(),
				hour   : this.getT(date.getHours(), 0),
				minute : this.getT(date.getMinutes(), 0),
				second : this.getT(date.getSeconds(), 0)
			});
			
			return this;
		},
		/**
		 * 绑定事件，顺便给body绑定一个onclick事件，用于关闭日历插件
		 * @param {Boolean} flag 是否绑定全部事件，值为false则只绑定日期
		 */
		bindEvents : function(flag /* Boolean */) {
			flag = flag !== false;
			
			var config  = _this.config,
				handler = _this.handler,
				tds     = handler.dateBar.getElementsByTagName('td');
			
			for (var i = 0, len = tds.length; i < len; ++i) {
				if (tds[i].innerHTML !== '-') {
					Ly.addEvent('onclick', function() {
						config.currentDate.date = this.innerHTML;
						this.className += ' ly-calendar-date-selected';
						config.previous && (config.previous.className = config.previous.className.replace(/ ly\-calendar\-date\-selected/g, ''));
						config.previous = this;
						_this.private.updateAcceptor();
						
						!config.showTimeBar && _this.private.hideCalendar();
					}, tds[i]);
					Ly.addEvent('onmouseover', function() {
						this.className += ' ly-calendar-date-tmpselect';
					}, tds[i]);
					Ly.addEvent('onmouseout', function() {
						this.className = this.className.replace(/ ly\-calendar\-date\-tmpselect/g, '');
					}, tds[i]);
				}
			}
			if (!flag) { return _this; }
			
			Ly.addEvent('onclick', function() {
				--config.currentDate.year;
				_this.private.updateDate();
				_this.private.updateAcceptor();
			}, handler.siblingYear);
			
			Ly.addEvent('onclick', function() {
				config.currentDate.month = --config.currentDate.month == -1 ? (--config.currentDate.year, 11) : config.currentDate.month;
				_this.private.updateDate();
				_this.private.updateAcceptor();
			}, handler.siblingMonth);
			
			Ly.addEvent('onclick', function() {
				config.currentDate.month = ++config.currentDate.month == 12 ? (++config.currentDate.year, 0) : config.currentDate.month;
				_this.private.updateDate();
				_this.private.updateAcceptor();
			}, handler.nextMonth);
			
			Ly.addEvent('onclick', function() {
				++config.currentDate.year;
				_this.private.updateDate();
				_this.private.updateAcceptor();
			}, handler.nextYear);
			
			Ly.addEvent('onchange', function() {
				config.currentDate.hour = parseInt(this.value, 10);
				
				_this.private.updateAcceptor();
			}, handler.hour);
			
			Ly.addEvent('onchange', function() {
				config.currentDate.minute = parseInt(this.value, 10);
				_this.private.updateAcceptor();
			}, handler.minute);
			
			Ly.addEvent('onchange', function() {
				config.currentDate.second = parseInt(this.value, 10);
				_this.private.updateAcceptor();
			}, handler.second);
			
			Ly.addEvent('onclick', function(obj, evt) {
				var target;
				
				evt = evt || window.event;
				target= evt.target || evt.srcElement;
				
				if (target !== _this.config.acceptor && !_this.private.own(target)) {
					_this.private.hideCalendar();
				}
			}, document);
			
			return this;
		},
		// 创建日历插件的标题头，用来显示年与月
		createTitleBar : function() {
			var config  = _this.config,
				handler = _this.handler;
			
			handler.siblingYear = Ly.createElement('a', {
				id        : 'ly-calendar-titlebar-siblingyear-' + config.id,
				cls       : 'ly-calendar-titlebar-siblingyear',
				href      : 'javascript:void(0);',
				innerHTML : '&lt;&lt;'
			});
			handler.nextYear = Ly.createElement('a', {
				id        : 'ly-calendar-titlebar-nextyear-' + config.id,
				cls       : 'ly-calendar-titlebar-nextyear',
				href      : 'javascript:void(0);',
				innerHTML : '&gt;&gt;'
			});
			handler.siblingMonth = Ly.createElement('a', {
				id        : 'ly-calendar-titlebar-siblingmonth-' + config.id,
				cls       : 'ly-calendar-titlebar-siblingmonth',
				href      : 'javascript:void(0);',
				innerHTML : '&lt;'
			});
			handler.nextMonth = Ly.createElement('a', {
				id        : 'ly-calendar-titlebar-nextmonth-' + config.id,
				cls       : 'ly-calendar-titlebar-nextmonth',
				href      : 'javascript:void(0);',
				innerHTML : '&gt;'
			});
			
			handler.year    = Ly.createElement('a', {
				id        : 'ly-calendar-titlebar-year-' + config.id,
				cls       : 'ly-calendar-titlebar-year',
				href      : 'javascript:void(0);',
				innerHTML : config.currentDate.year
			});
			handler.month   = Ly.createElement('a', {
				id        : 'ly-calendar-titlebar-month-' + config.id,
				cls       : 'ly-calendar-titlebar-month',
				href      : 'javascript:void(0);',
				innerHTML : Ly.fill(config.currentDate.month + 1)
			});

			handler.titleBar.appendChild(handler.siblingYear);
			handler.titleBar.appendChild(handler.siblingMonth);
			handler.titleBar.appendChild(handler.year);
			handler.titleBar.appendChild(Ly.createElement('span', { cls : 'ly-calendar-titlebar-split', innerHTML : '-' }));
			handler.titleBar.appendChild(handler.month);
			handler.titleBar.appendChild(handler.nextMonth);
			handler.titleBar.appendChild(handler.nextYear);
			
			return this;
		},
		// 创建日历插件的日期块
		createDateBar : function() {
			var table   = '<table class="dateBar" border="0" cellspacing="1" cellpadding="0"><tbody>',
				handler = _this.handler,
				config  = _this.config,
				year    = config.currentDate.year,
				month   = config.currentDate.month,
				day     = config.currentDate.date,
				week    = config.currentDate.week,
				weekBar = function() {
					var w = [ '日', '一', '二', '三', '四', '五', '六' ],
						str = '';
					
					for (var i = 0, len = w.length; i < len; ++i) {
						str += '<th>' + w[i] + '</th>';
					}
					
					return str;
				}(),
				dateBar = function() {
					var f     = new Date(year, month, 1).getDay(),      // 获取月的第一天的星期
						e     = new Date(year, month + 1, 0).getDay(),  // 获取月的最后一天的星期
						k     = new Date().getFullYear() == year && new Date().getMonth() == month,
						dates = new Date(year, month + 1, 0).getDate(),
						cd    = new Date().getDate(),
						d     = '',
						i     = 1,
						cls   = 'ly-calendar-date';
					
					for (var j = 0; j < 42; ++j) {
						!j && (d += '<tr>');
						!(j % 7) && (d += '</tr><tr>');
						
						if (j < f) {
							d += '<td class="ly-calendar-date-gray">-</td>';
						} else if (i <= dates) {
							cls = 'ly-calendar-date';
							i == day && (cls = 'ly-calendar-date-selected');
							i == cd && k && (cls = 'ly-calendar-date-current');
							d += '<td class="' + cls + '">' + (i++) + '</td>';
						} else {
							d += '<td class="ly-calendar-date-gray">-</td>';
						}
					}
					
					return d;
				}();
			
			table += weekBar + dateBar + '</tbody></table>';
			
			handler.dateBar.innerHTML = table;
			
			return this;
		},
		// 若showTimeBar=true则创建时间选项
		createTimeBar : function() {
			var config      = _this.config,
				handler     = _this.handler,
				initOptions = function(start, end, step, current) {
					if (!step) { return arguments.callee; }
					current = current > end ? end : current;
					for (var i = start; i <= end; i += step) {
						this.options[this.length] = new Option(Ly.fill(i), i);
						if (current - i < step && current - i > -1) {
							this.options[this.length - 1].selected = true;
						}
					}
					return arguments.callee;
				},
				tmp;
			
			if (!config.showTimeBar) {
				handler.timeBar.style.display = 'none';
				return this;
			}
			
			handler.win.style.height = '240px';
			
			handler.hour   = Ly.createElement('select', { cls : 'ly-calendar-timebar-time' });
			handler.minute = Ly.createElement('select', { cls : 'ly-calendar-timebar-time' });
			handler.second = Ly.createElement('select', { cls : 'ly-calendar-timebar-time' });
			
			handler.timeBar.appendChild(Ly.createElement('span', {
				cls : 'ly-calendar-timebar-tag',
				innerHTML : '时间:'
			}));
			handler.timeBar.appendChild(handler.hour);
			handler.timeBar.appendChild(Ly.createElement('span', {
				cls : 'ly-calendar-timebar-separator',
				innerHTML : ':'
			}));
			handler.timeBar.appendChild(handler.minute);
			handler.timeBar.appendChild(Ly.createElement('span', {
				cls : 'ly-calendar-timebar-separator',
				innerHTML : ':'
			}));
			handler.timeBar.appendChild(handler.second);
			
			initOptions.call(
				handler.hour,
				0,
				23,
				tmp = config.timeStep.length > 0 ? config.timeStep[0] : 0,
				config.currentDate.hour = !tmp ? config.currentDate.hour : parseInt(config.currentDate.hour / config.timeStep[0]) * config.timeStep[0]
			).call(
				handler.minute,
				0,
				59,
				tmp = config.timeStep.length > 1 ? config.timeStep[1] : 0,
				config.currentDate.minute = !tmp ? config.currentDate.minute : parseInt(config.currentDate.minute / config.timeStep[1]) * config.timeStep[1]
			).call(
				handler.second,
				0,
				59,
				tmp = config.timeStep.length > 2 ? config.timeStep[2] : 0,
				config.currentDate.second = !tmp ? config.currentDate.second : parseInt(config.currentDate.second / config.timeStep[2]) * config.timeStep[2]
			);
			
			return this;
		},
		// 创建操作按钮区
		createOperateBar : function() {
			var config           = _this.config,
				handler          = _this.handler,
				currentDayButton = Ly.createElement('button', {
					id        : 'ly-calendar-operatebar-currentdaybutton-' + config.id,
					cls       : 'ly-calendar-operatebar-currentdaybutton',
					innerHTML : '今天'
				}),
				closeButton      = Ly.createElement('button', {
					id        : 'ly-calendar-operatebar-closebutton-' + config.id,
					cls       : 'ly-calendar-operatebar-closebutton',
					innerHTML : '关闭'
				});
			
			handler.operateBar.appendChild(currentDayButton);
			handler.operateBar.appendChild(closeButton);
			
			Ly.addEvent('onclick', function() {
				_this.private.updateDate(new Date());
				_this.private.updateTime(new Date());
				_this.private.updateAcceptor();
			}, currentDayButton);
			Ly.addEvent('onclick', function() {
				_this.private.hideCalendar();
			}, closeButton);
			
			return this;
		},
		// 创建框架元素
		createElement : function() {
			var handler = _this.handler,
				config  = _this.config,
				id      = config.id;
			
			handler.win = Ly.createElement('div', {
				id  : 'ly-calendar-win-' + id,
				cls : 'ly-calendar-win',
				style : 'top:' + (Ly.getY(config.acceptor) + config.acceptor.offsetHeight) + 'px;' +
					    'left:' + Ly.getX(config.acceptor) + 'px;'
			});
			handler.titleBar = Ly.createElement('div', {
				id  : 'ly-calendar-titlebar-' + id,
				cls : 'ly-calendar-titlebar'
			});
			handler.dateBar = Ly.createElement('div', {
				id  : 'ly-calendar-datebar-' + id,
				cls : 'ly-calendar-datebar'
			});
			handler.timeBar = Ly.createElement('div', {
				id  : 'ly-calendar-timebar-' + id,
				cls : 'ly-calendar-timebar'
			});
			handler.operateBar = Ly.createElement('div', {
				id  : 'ly-calendar-operatebar-' + id,
				cls : 'ly-calendar-operatebar'
			});
			
			handler.win.appendChild(handler.titleBar);
			handler.win.appendChild(handler.dateBar);
			handler.win.appendChild(handler.timeBar);
			handler.win.appendChild(handler.operateBar);
			config.container.appendChild(handler.win);
			
			return this;
		}
	};
};

Ly.Calendar.prototype = {
	version : '1.0a',
	// 渲染日历插件
	render  : function() {
		this.private.initialization();
		return this;
	},
	// 隐藏日历插件
	hide : function() {
		this.private.hideCalendar();
		return this;
	},
	// 显示日历插件
	show : function() {
		// 用接收器中的日期初始化defaultDate
		var dd = this.config.acceptor.value;
		
		if (dd) {
			dd = dd.split(/\D+/);
			// 对两位年份初齐，其余的情况忽略
			if (dd[0].length == 2) {
				dd[0] = [new Date().getFullYear().toString().substr(0, 2), dd[0]].join('');
			}
			
			// 对月份进行处理
			dd[1] = parseInt(dd[1], 10) - 1;
			
			dd = this.config.defaultDate = eval('new Date(' + dd.join(',') + ')');
			
			this.private.updateDate(dd).updateTime(dd);
		}
		this.private.showCalendar();
		
		return this;
	},
	// 获取当前日期
	getSelectedDate : function() {
		var d = this.config.currentDate;
		
		return Ly.formatTime(new Date(d.year, d.month, d.date, d.hour, d.minute, d.second), this.config.formatDate);
	}
};